package com.google.zxing.slice;

import com.google.zxing.*;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.ToastDialog;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.agp.window.service.WindowManager;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraConfig;
import ohos.media.camera.device.CameraStateCallback;
import ohos.media.camera.device.FrameConfig;
import ohos.media.common.BufferInfo;
import ohos.media.image.Image;
import ohos.media.image.ImageReceiver;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageFormat;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;
import ohos.vibrator.agent.VibratorAgent;

import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.nio.ByteBuffer;
import java.util.*;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PREVIEW;
import static ohos.media.camera.params.Metadata.FlashMode.FLASH_ALWAYS_OPEN;
import static ohos.media.camera.params.Metadata.FlashMode.FLASH_CLOSE;

public class CaptureAbilitySlice extends AbilitySlice {
    private HiLogLabel hiLogLabel = new HiLogLabel(HiLog.LOG_APP,0,"chencongxiao");
//    private ImageScanner scanner;
    private DirectionalLayout myLayout;
    private ImageReceiver imageReceiver;
    private CameraKit cameraKit;
    private SurfaceProvider surfaceProvider;
    private Surface previewSurface;
    private Surface dataSurface;
    private Camera mcamera;
    private Text scanText,flashTv;
    private ohos.agp.components.Image imageIv;
    private boolean barcodeScanned = false;
    private boolean previewing = true;
    private List<String> list;
    private EventRunner runner;
    private EventHandler handler;
    private Button mBtn4;
    private final static int SELECT_PHOTO_CODE = 1000;


    public static int VIDEO_WIDTH = 640;
    public static int VIDEO_HEIGHT = 480;
    private boolean isFlashOpen=false;
    private FrameConfig.Builder framePreviewConfigBuilder;
    private DataAbilityHelper helper;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_capture);

        helper = DataAbilityHelper.creator(this);
        runner = EventRunner.getMainEventRunner();
        handler = new EventHandler(runner);

//        scanner = new ImageScanner();
//        scanner.setConfig(0, Config.X_DENSITY, 3);
//        scanner.setConfig(0, Config.Y_DENSITY, 3);

        DisplayAttributes displayAttributes = DisplayManager.getInstance().getDefaultDisplay(this).get().getAttributes();
        VIDEO_WIDTH=displayAttributes.width;
        VIDEO_HEIGHT=displayAttributes.height;
        //初始化UI和相机，实现视频帧的获取
        initUIandCamera();
        //注册编码器，实现视频帧的编码
        registerEncoder();
    }
    @Override
    protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
        super.onAbilityResult(requestCode, resultCode, resultData);
        switch (requestCode){
            case SELECT_PHOTO_CODE:
                String uriStr=resultData.getUriString();
                String mediaId=uriStr.substring(uriStr.lastIndexOf("/"));
                getImage(mediaId.replace("/",""));
                break;
        }
    }

    private void initUIandCamera(){
        //布局容器
        myLayout = (DirectionalLayout) findComponentById(ResourceTable.Id_direction_layout);//new DirectionalLayout(this);
        DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT);
        myLayout.setLayoutConfig(config);
//        myLayout.setOrientation(Component.VERTICAL);
//        myLayout.setPadding(32,32,32,32);
        //SurfaceProvider，用于播放摄像头画面
        config.width = VIDEO_WIDTH;
        config.height = VIDEO_HEIGHT;
        config.alignment = LayoutAlignment.HORIZONTAL_CENTER;
        DependentLayout dependentLayout= (DependentLayout) findComponentById(ResourceTable.Id_depend_layout);
//        DependentLayout.LayoutConfig layoutConfig=new DependentLayout.LayoutConfig(VIDEO_WIDTH, VIDEO_HEIGHT);
//        dependentLayout.setLayoutConfig(layoutConfig);
        surfaceProvider = new SurfaceProvider(this);
        surfaceProvider.setLayoutConfig(new DependentLayout.LayoutConfig(VIDEO_WIDTH, VIDEO_HEIGHT));
        surfaceProvider.getSurfaceOps().get().addCallback(callback);
        surfaceProvider.pinToZTop(false);
        WindowManager.getInstance().getTopWindow().get().setTransparent(true);
        ScanBoxView mScanBoxView = new ScanBoxView(this);
        dependentLayout.addComponent(surfaceProvider,0);
        dependentLayout.addComponent(mScanBoxView,1,new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_PARENT,DirectionalLayout.LayoutConfig.MATCH_PARENT));
        //Button，点击开始编码和发送
        config.height = DirectionalLayout.LayoutConfig.MATCH_CONTENT;
        config.width = DirectionalLayout.LayoutConfig.MATCH_CONTENT;
        config.setMargins(0,50,0,0);
        scanText = (Text) findComponentById(ResourceTable.Id_tv_scanText);
        scanText.setTextSize(100);
        scanText.setMultipleLine(true);
        scanText.setText("扫描中" );
//        scanText.setLayoutConfig(config);

        flashTv = (Text) findComponentById(ResourceTable.Id_tv_flashTv);
        flashTv.setTextSize(100);
        flashTv.setMultipleLine(true);
        flashTv.setText("开启闪光灯" );
//        flashTv.setLayoutConfig(config);
        flashTv.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                if(framePreviewConfigBuilder!=null){
                    if(isFlashOpen){
                        isFlashOpen=false;
                        framePreviewConfigBuilder.setFlashMode(FLASH_CLOSE);
                        flashTv.setText("开启闪光灯" );
                    }else {
                        flashTv.setText("关闭闪光灯");
                        isFlashOpen=true;
                        framePreviewConfigBuilder.setFlashMode(FLASH_ALWAYS_OPEN);
                    }
                    mcamera.triggerLoopingCapture(framePreviewConfigBuilder.build());
                }
            }
        });
        imageIv= (ohos.agp.components.Image) findComponentById(ResourceTable.Id_iv_image);
//        imageIv.setLayoutConfig(config);
//        myLayout.addComponent(dependentLayout);
//        myLayout.addComponent(scanText);
//        myLayout.addComponent(flashTv);
//        myLayout.addComponent(imageIv);

        mBtn4 = (Button) findComponentById(ResourceTable.Id_photo);
        mBtn4.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                selectedPicture();
            }
        });
        super.setUIContent(myLayout);
    }
    //打开相册
    public void selectedPicture() {
        try {
            Intent intent = new Intent();
            intent.setAction("android.intent.action.PICK");
            intent.addFlags(Intent.FLAG_NOT_OHOS_COMPONENT);
            intent.setType("image/*");
            startAbilityForResult(intent,SELECT_PHOTO_CODE);
        } catch (Exception ev) {
            ev.printStackTrace();
        }
    }
    private void registerEncoder() {
        imageReceiver = ImageReceiver.create(640, 480, ImageFormat.YUV420_888, 5);
        IImageArrivalListenerImpl listener = new IImageArrivalListenerImpl();
        imageReceiver.setImageArrivalListener(listener);
    }
    //监听器，当有数据进入ImageReceiver时触发
    private class IImageArrivalListenerImpl implements ImageReceiver.IImageArrivalListener {
        //对监听事件的响应逻辑，实现对图像数据处理和到编码器的传输
        @Override
        public void onImageArrival(ImageReceiver imageReceiver) {
            HiLog.fatal(hiLogLabel,"扫描结果: " + "接收到数据");
            Image mImage = imageReceiver.readNextImage();
            if (mImage != null) {
                BufferInfo bufferInfo = new BufferInfo();
                ByteBuffer mBuffer;
                byte[] YUV_DATA = new byte[480 * 640 * 3 / 2];
                int i;
                //采集YUV格式数据
                mBuffer = mImage.getComponent(ImageFormat.ComponentType.YUV_Y).getBuffer();
                for (i = 0; i < 640 * 480; i++) {
                    YUV_DATA[i] = mBuffer.get(i);
                }
                mBuffer = mImage.getComponent(ImageFormat.ComponentType.YUV_V).getBuffer();
                for (i = 0; i < 640 * 480 / 4; i++) {
                    YUV_DATA[(640 * 480) + i * 2] =
                            mBuffer.get(i * 2);
                }
                mBuffer = mImage.getComponent(ImageFormat.ComponentType.YUV_U).getBuffer();
                for (i = 0; i < 640 * 480 / 4; i++) {
                    YUV_DATA[(640 * 480) + i * 2 + 1] = mBuffer.get(i * 2);
                }
                bufferInfo.setInfo(0, 640 * 480 * 3 / 2, mImage.getTimestamp(), 0);

//                Image.Component component = mImage.getComponent(ImageFormat.ComponentType.JPEG);
//                byte[] bytes = new byte[component.remaining()];
//                component.read(bytes);

                decode(YUV_DATA,640,480);

//                Image barcode =  new Image(mImage.getImageSize().width, mImage.getImageSize().height, "Y800");
//                barcode.setData(YUV_DATA);
//
//                int result = scanner.scanImage(barcode);
//
//                if (result != 0) {
//
//                    HiLog.fatal(hiLogLabel,"succeed");
//                    SymbolSet syms = scanner.getResults();
//                    HiLog.fatal(hiLogLabel, String.valueOf(syms.size()));
//                    list = new ArrayList<>(syms.size());
//
//                    for (Symbol sym : syms) {
//                        handler.postTask(new Runnable() {
//                            @Override
//                            public void run() {
//                                scanText.setText("扫描结果:" + sym.getData());
//                                scanText.invalidate();
//
//                            }
//                        });
//                        HiLog.fatal(hiLogLabel,"扫描结果: " + sym.getData());
//
//                        barcodeScanned = true;
//
//                    }
//                }



                mImage.release();
                return;
            }
        }
    }
    private void openCamera(){
        // 获取 CameraKit 对象
        cameraKit = CameraKit.getInstance(this);
        String cameraId =getCameraId();
        if (cameraKit == null) {
            return;
        }
        try {
            // 获取当前设备的逻辑相机列表cameraIds
            if (cameraId==null) {
                System.out.println("cameraIds size is 0");
                return;
            }
            // 用于相机创建和相机运行的回调
            CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
            if(cameraStateCallback ==null) {
                System.out.println("cameraStateCallback is null");
            }
            // 创建用于运行相机的线程
            EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb"));
            if(eventHandler ==null) {
                System.out.println("eventHandler is null");
            }
            // 创建相机！
            cameraKit.createCamera(cameraId, cameraStateCallback, eventHandler);
        } catch (IllegalStateException e) {
            System.out.println("getCameraIds fail");
        }
        initMutiFormat();
    }
    private String cameraId = null;
    public String getCameraId(){
        if (cameraId == null){
            String[] cameraIds = cameraKit.getCameraIds();
            if (cameraIds != null && cameraIds.length>0){
                cameraId = cameraIds[0];
            }
        }

        return cameraId;
    }

    private final class CameraStateCallbackImpl extends CameraStateCallback {
        //相机回调
        @Override
        public void onCreated(Camera camera) {
            mcamera = camera;
            //相机创建时回调
            CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
            if (cameraConfigBuilder == null) {
                System.out.println("onCreated cameraConfigBuilder is null");
                return;
            }
            // 配置预览的 Surface
            cameraConfigBuilder.addSurface(previewSurface);
            // 配置拍照的 Surface
            dataSurface = imageReceiver.getRecevingSurface();
            cameraConfigBuilder.addSurface(dataSurface);
            try {
                // 相机设备配置
                camera.configure(cameraConfigBuilder.build());
            } catch (IllegalArgumentException e) {
                System.out.println("Argument Exception");
            } catch (IllegalStateException e) {
                System.out.println("State Exception");
            }
        }
        @Override
        public void onConfigured(Camera camera) {
            framePreviewConfigBuilder = mcamera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
            // 配置预览 Surface
            framePreviewConfigBuilder.addSurface(previewSurface);
            // 配置拍照的 Surface
            framePreviewConfigBuilder.addSurface(dataSurface);
//                    frameConfigBuilder.setAfMode(1);
            try {
                // 启动循环帧捕获
                int triggerId = mcamera.triggerLoopingCapture(framePreviewConfigBuilder.build());
            } catch (IllegalArgumentException e) {
                System.out.println("Argument Exception");
            } catch (IllegalStateException e) {
                System.out.println("State Exception");
            }
            //相机配置

        }
        @Override
        public void onReleased(Camera camera) {
            // 释放相机设备
            if (mcamera != null) {
//                previewing = false;
//                mcamera.stopLoopingCapture();
                mcamera.release();
                mcamera = null;
            }
        }
    }

    private SurfaceOps.Callback callback = new SurfaceOps.Callback() {
        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
            //将SurfaceProvider中的Surface与previewsurface建立连接
            previewSurface = surfaceOps.getSurface();
            //初始化相机
            openCamera();
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
        }
    };
    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
    MultiFormatReader multiFormatReader;
    private void initMutiFormat(){
        multiFormatReader = new MultiFormatReader();
        // 解码的参数
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(
                2);
        // 可以解析的编码类型
        Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
        if (decodeFormats == null || decodeFormats.isEmpty()) {
            decodeFormats = new Vector<BarcodeFormat>();

            // 这里设置可扫描的类型，我这里选择了都支持
            decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
            decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
            decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
        }
        hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);

        // 设置继续的字符编码格式为UTF8
        hints.put(DecodeHintType.CHARACTER_SET, "UTF8");

        // 设置解析配置参数
        multiFormatReader.setHints(hints);
    }
    private boolean isGetCode=true;
    private void decode(byte[] data, int width, int height) {
        long start = System.currentTimeMillis();
        Result rawResult = null;

//        byte[] rotatedData = new byte[data.length];
//        for (int y = 0; y < height; y++) {
//            for (int x = 0; x < width; x++)
//                rotatedData[x * height + height - y - 1] = data[x + y * width];
//        }
//        int tmp = width;
//        width = height;
//        height = tmp;

//        PlanarYUVLuminanceSource source = activity.getCameraManager()
//                .buildLuminanceSource(rotatedData, width, height);
        PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, width, height, 0, 0,width, height, false);
        if (source != null) {
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            try {
                // 预览界面最终取到的是个bitmap，然后对其进行解码
                rawResult = multiFormatReader.decodeWithState(bitmap);
            } catch (ReaderException re) {
                // continue
            } finally {
                multiFormatReader.reset();
            }
        }
        if(rawResult!=null&&isGetCode){
            isGetCode=false;
            getGlobalTaskDispatcher(TaskPriority.DEFAULT).delayDispatch(new Runnable() {
                @Override
                public void run() {
                    isGetCode=true;
                }
            },3000);
            vibrate();
            String result = ResultParser.parseResult(rawResult).toString();
            System.out.println(""+result);
            ImageSource imageSource = null;
            PixelMap pixelMap = null;
            try {
                imageSource = ImageSource.create(data, null);
                pixelMap = imageSource.createPixelmap(null);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (imageSource != null) {
                    imageSource.release();
                }
            }
            PixelMap finalPixelMap = pixelMap;
            getUITaskDispatcher().syncDispatch(new Runnable() {
                @Override
                public void run() {
                    scanText.setText("扫描的结果是："+result);
                    if(finalPixelMap!=null){
                        imageIv.setPixelMap(finalPixelMap);
                    }

                }
            });
//            terminateAbility(10086);
//            showToast("解析结果"+result)
        }
    }
    public static Size getBestSize(List<Size> sizes,int width,int height){
        Size size = null;
        float rate = Integer.MAX_VALUE;
        for (Size curSize:sizes){
            float mRate = Math.max(Math.abs(curSize.width/(float)width),Math.abs(curSize.height/(float)height));
            if (rate>mRate){
                size = curSize;
                rate = mRate;
            }
        }
        return size;



    }
    //扫到结果震动提示一下
    private void vibrate() {
        VibratorAgent vibratorAgent = new VibratorAgent();
        List<Integer> vids = vibratorAgent.getVibratorIdList();
        for(Integer position:vids) {
            if(vibratorAgent.isSupport(position)) {
                vibratorAgent.startOnce(position, 200);
                break;
            }
        }
    }
    private void showToast(String msg) {
        new ToastDialog(this).setContentText(msg).show();
    }
    private void getImage(String mediaId){
        if(mediaId.isEmpty()){
            return;
        }
        try {
            Uri videoUri=Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,""+mediaId);
            FileDescriptor filedesc = helper.openFile(videoUri,"r");
            ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
            srcOpts.formatHint = "image/png";
            ImageSource imageSource=ImageSource.create(filedesc,srcOpts);
            //创建图像解码选项
            ImageSource.DecodingOptions decOPtions = new ImageSource.DecodingOptions();
            decOPtions.desiredPixelFormat = PixelFormat.ARGB_8888;
            decOPtions.desiredSize=new Size(640,640);
            PixelMap pixelMap=imageSource.createPixelmap(decOPtions);
            if(pixelMap!=null){
                imageIv.setPixelMap(pixelMap);
            }
            getGlobalTaskDispatcher(TaskPriority.DEFAULT).asyncDispatch(new Runnable() {
                @Override
                public void run() {
                    String rawResult=syncDecodeQRCode(pixelMap);
                    if(rawResult==null){
                        getUITaskDispatcher().syncDispatch(new Runnable() {
                            @Override
                            public void run() {
                                showToast("解析结果为空");

                            }
                        });
                        return;
                    }
                    vibrate();
                    getUITaskDispatcher().asyncDispatch(new Runnable() {
                        @Override
                        public void run() {
                            scanText.setText("扫描的结果是："+rawResult);
                        }
                    });
                }
            });
        } catch (DataAbilityRemoteException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 同步解析bitmap二维码。该方法是耗时操作，请在子线程中调用。
     *
     * @param bitmap 要解析的二维码图片
     * @return 返回二维码图片里的内容 或 null
     */
    static final Map<DecodeHintType, Object> ALL_HINT_MAP = new EnumMap<>(DecodeHintType.class);
    public static String syncDecodeQRCode(PixelMap bitmap) {
        Result result;
        RGBLuminanceSource source = null;
        try {
            int width = bitmap.getImageInfo().size.width;
            int height = bitmap.getImageInfo().size.height;
            int[] pixels = new int[width * height];
            bitmap.readPixels(pixels, 0, width, new Rect(0,0,width,height));
            source = new RGBLuminanceSource(width, height, pixels);
            result = new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(source)), ALL_HINT_MAP);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
            if (source != null) {
                try {
                    result = new MultiFormatReader().decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)), ALL_HINT_MAP);
                    return result.getText();
                } catch (Throwable e2) {
                    e2.printStackTrace();
                }
            }
            return null;
        }
    }
    static {
        List<BarcodeFormat> allFormatList = new ArrayList<>();
        allFormatList.add(BarcodeFormat.AZTEC);
        allFormatList.add(BarcodeFormat.CODABAR);
        allFormatList.add(BarcodeFormat.CODE_39);
        allFormatList.add(BarcodeFormat.CODE_93);
        allFormatList.add(BarcodeFormat.CODE_128);
        allFormatList.add(BarcodeFormat.DATA_MATRIX);
        allFormatList.add(BarcodeFormat.EAN_8);
        allFormatList.add(BarcodeFormat.EAN_13);
        allFormatList.add(BarcodeFormat.ITF);
        allFormatList.add(BarcodeFormat.MAXICODE);
        allFormatList.add(BarcodeFormat.PDF_417);
        allFormatList.add(BarcodeFormat.QR_CODE);
        allFormatList.add(BarcodeFormat.RSS_14);
        allFormatList.add(BarcodeFormat.RSS_EXPANDED);
        allFormatList.add(BarcodeFormat.UPC_A);
        allFormatList.add(BarcodeFormat.UPC_E);
        allFormatList.add(BarcodeFormat.UPC_EAN_EXTENSION);

        // 可能的编码格式
        ALL_HINT_MAP.put(DecodeHintType.POSSIBLE_FORMATS, allFormatList);
        // 花更多的时间用于寻找图上的编码，优化准确性，但不优化速度
        ALL_HINT_MAP.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        // 复杂模式，开启 PURE_BARCODE 模式（带图片 LOGO 的解码方案）
//        ALL_HINT_MAP.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
        // 编码字符集
        ALL_HINT_MAP.put(DecodeHintType.CHARACTER_SET, "utf-8");
    }
}
